package config

import (
	"log"
	"sync"

	"github.com/fsnotify/fsnotify"
	"github.com/spf13/viper"
)

type Config struct {
	App   AppConfig
	MySQL MySQLConfig
	Redis RedisConfig
	JWT   JWTConfig
	Log   LogConfig
}

type AppConfig struct {
	Name  string
	Env   string
	Port  int
	Debug bool
}

type MySQLConfig struct {
	Host            string
	Port            int
	User            string
	Password        string
	DBName          string
	MaxOpenConns    int `mapstructure:"max_open_conns"`
	MaxIdleConns    int `mapstructure:"max_idle_conns"`
	ConnMaxLifetime int `mapstructure:"conn_max_lifetime"`
}

type RedisConfig struct {
	Host     string
	Port     int
	Password string
	DB       int
	PoolSize int `mapstructure:"pool_size"`
}

type JWTConfig struct {
	Secret string
	Expire int
}

type LogConfig struct {
	Level      string
	Path       string
	MaxSize    int `mapstructure:"max_size"`
	MaxBackups int `mapstructure:"max_backups"`
	MaxAge     int `mapstructure:"max_age"`
	Compress   bool
}

var (
	once     sync.Once
	instance *Config
)

func Load() *Config {
	once.Do(func() {
		viper.SetConfigName("config")
		viper.SetConfigType("yaml")
		viper.AddConfigPath("./config")
		viper.AddConfigPath(".")

		if err := viper.ReadInConfig(); err != nil {
			log.Fatalf("Error reading config file, %s", err)
		}

		instance = &Config{}
		if err := viper.Unmarshal(instance); err != nil {
			log.Fatalf("Unable to decode into struct, %v", err)
		}

		// Watch for changes in the config file
		viper.WatchConfig()
		viper.OnConfigChange(func(e fsnotify.Event) {
			log.Println("Config file changed:", e.Name)
			if err := viper.Unmarshal(instance); err != nil {
				log.Printf("Unable to decode config changes, %v", err)
			}
		})
	})

	return instance
}

func GetConfig() *Config {
	if instance == nil {
		return Load()
	}
	return instance
}

func Env() string {
	return instance.App.Env
}

func IsDev() bool {
	return instance.App.Env == "development"
}

func IsProd() bool {
	return instance.App.Env == "production"
}
